package com.xgk.boot.module.core.service.customer;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.xgk.boot.framework.redis.core.RedisLockManager;
import com.xgk.boot.module.core.dal.entity.customer.CustomerBalanceDO;
import com.xgk.boot.module.core.dal.entity.payment.TemporaryCreditDO;
import com.xgk.boot.module.core.dal.entity.truck.TruckOrdersDO;
import com.xgk.boot.module.core.dal.iservice.CustomerBalanceService;
import com.xgk.boot.module.core.dal.mapper.customer.CustomerBalanceMapper;
import com.xgk.boot.module.core.dal.mapper.payment.TemporaryCreditMapper;
import com.xgk.boot.module.core.dal.redis.RedisLockConstants;
import com.xgk.boot.module.core.enums.biz.CurrencyEnum;
import com.xgk.boot.module.core.service.truck.TruckOrderLogServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.xgk.boot.module.core.enums.LogRecordConstants.*;

@Slf4j
@Service
public class CustomerBalanceServiceImpl extends ServiceImpl<CustomerBalanceMapper, CustomerBalanceDO> implements CustomerBalanceService {

    @Resource
    private TruckOrderLogServiceImpl orderLogService;
    @Resource
    private RedisLockManager lockManager;
    @Resource
    private TemporaryCreditMapper temporaryCreditMapper;

    public List<CustomerBalanceDO> listCustBalances(Collection<String> custCodes){
        if(CollUtil.isEmpty(custCodes)){
            return Lists.newArrayList();
        }
        LambdaQueryWrapperX<CustomerBalanceDO> in = new LambdaQueryWrapperX<CustomerBalanceDO>().in(CustomerBalanceDO::getCustomerCode, custCodes);
        return this.list(in);
    }

    public  CustomerBalanceDO getCustBalanceByCustCode(String custCode){
        LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, custCode);
        return this.getOne(eq);
    }

    public void createCustomerBalance(Long customerId,String customerCode){
        CustomerBalanceDO customerBalanceDO = new CustomerBalanceDO();
        customerBalanceDO.setCustomerId(customerId);
        customerBalanceDO.setCustomerCode(customerCode);
        customerBalanceDO.setCurrencyCode(CurrencyEnum.USD.getValue());
        customerBalanceDO.setCbValue(BigDecimal.ZERO);
        customerBalanceDO.setCbCreditLine(BigDecimal.ZERO);
        customerBalanceDO.setCbHoldValue(BigDecimal.ZERO);
        customerBalanceDO.setCreditBalance(BigDecimal.ZERO);
        this.save(customerBalanceDO);
    }


    /**
     * 下单前根据估算，冻结对应费用
     * 增加锁定费用，并记录订单操作日志
     * @param addLockFees
     * @param ordersDO
     */
    @Transactional
    public void addCustomeLockingFees(BigDecimal addLockFees, TruckOrdersDO ordersDO) {

        //获取锁，直接add字段
        String lockName = RedisLockConstants.CUSTOMER_FEE + ordersDO.getCustomerCode();
        boolean locked = false;
        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额锁失败,{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, ordersDO.getCustomerCode());
            CustomerBalanceDO one = this.getOne(eq);
            one.setCbHoldValue(one.getCbHoldValue().add(addLockFees));
            this.updateById(one);
            //记录日志
            orderLogService.addTruckOrderLog(ordersDO, "Increase order forecasting fee:{"+addLockFees+"}");
        } catch (Exception e) {
            log.error("订单预扣费失败,订单号：{}, 错误信息：{}", ordersDO.getOrderCode(), e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }

    /**
     * 回退客户冻结金额（下单完成或下单失败）
     * @param addLockFees
     * @param ordersDO
     * @param note
     */
    @Transactional
    public void reduceCustomeLockingFees(BigDecimal addLockFees, TruckOrdersDO ordersDO, String note) {

        //获取锁，直接add字段
        String lockName = RedisLockConstants.CUSTOMER_FEE + ordersDO.getCustomerCode();
        boolean locked = false;
        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额扣费锁失败,order:{},lockName:{}",ordersDO.getOrderCode(), lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, ordersDO.getCustomerCode());
            CustomerBalanceDO one = this.getOne(eq);
            one.setCbHoldValue(one.getCbHoldValue().subtract(addLockFees));
            this.updateById(one);
            //记录日志
            orderLogService.addTruckOrderLog(ordersDO, note+",Refund order forecast fee:{"+addLockFees+"}");
        } catch (Exception e) {
            log.error("订单预扣费回退,订单号：{}, 错误信息：{}", ordersDO.getOrderCode(), e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }


    /**
     * 按照订单费用扣减客户余额
     * @param reduceFees 扣减的余额
     * @param ordersDO  关联订单信息
     * @param note 操作说明
     */
    @Transactional
    public void reduceCustomeFees(BigDecimal reduceFees, TruckOrdersDO ordersDO, String note) {
        String lockName = RedisLockConstants.CUSTOMER_FEE + ordersDO.getCustomerCode();
        boolean locked = false;

        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额扣费锁失败,order:{},lockName:{}",ordersDO.getOrderCode(), lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, ordersDO.getCustomerCode());
            CustomerBalanceDO one = this.getOne(eq);
            one.setCbValue(one.getCbValue().subtract(reduceFees));
            this.updateById(one);
            //记录日志
            orderLogService.addTruckOrderLog(ordersDO, note+",Order deduction fee:{"+reduceFees+"}");
        } catch (Exception e) {
            log.error("订单扣费异常,订单号：{}, 错误信息：{}", ordersDO.getOrderCode(), e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }

    /**
     * 回退金额值客户余额
     * @param reduceFees 扣减的余额
     * @param customerCode  关联订单信息
     */
    @Transactional
    public void rebackFeeToCustomeFees(BigDecimal reduceFees, String customerCode,TruckOrdersDO ordersDO) {
        String lockName = RedisLockConstants.CUSTOMER_FEE + customerCode;
        boolean locked = false;

        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额扣费锁失败,order:{},lockName:{}",customerCode, lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, ordersDO.getCustomerCode());
            CustomerBalanceDO one = this.getOne(eq);
            one.setCbValue(one.getCbValue().add(reduceFees));
            this.updateById(one);
            //记录日志
            orderLogService.addTruckOrderLog(ordersDO, "Order reback fee:{"+reduceFees+"}");
        } catch (Exception e) {
            log.error("订单扣费异常,订单号：{}, 错误信息：{}", ordersDO.getOrderCode(), e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }


    /**
     * 客户余额充值
     * 充值时需要核销授信，需要跟新授信记录表
     * @param cusotmerCode 客户代码
     * @param rechargeAmount 充值金额
     */
    @LogRecord(type = SYSTEM_PAYMENT_TYPE, subType = SYSTEM_PAYMENT_CUSTOMER_RECHARGE, bizNo = "{{#customerCode}}",
            success = SYSTEM_PAYMENT_CUSTOMER_RECHARGE_SUCCESS)
    @Transactional
    public void customerBalanceRecharge(String cusotmerCode,BigDecimal rechargeAmount){
        String lockName = RedisLockConstants.CUSTOMER_FEE + cusotmerCode;
        boolean locked = false;

        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额锁失败,ockName:{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.ACTION_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, cusotmerCode);
            CustomerBalanceDO one = this.getOne(eq);
            if(one == null){
                one = new CustomerBalanceDO();
                one.setCustomerCode(cusotmerCode);
                one.setCustomerId(0L);
                one.setCbCreditLine(BigDecimal.ZERO);
                one.setCreditBalance(BigDecimal.ZERO);
                one.setCbValue(BigDecimal.ZERO);
            }
            if(rechargeAmount.doubleValue()>0 && one.getCbCreditLine().doubleValue()>0){
                if(rechargeAmount.compareTo(one.getCbCreditLine())>=0){
                    rechargeAmount = rechargeAmount.subtract(one.getCbCreditLine());
                    one.setCbCreditLine(BigDecimal.ZERO);
                }else {
                    one.setCreditBalance(one.getCbCreditLine().subtract(rechargeAmount));
                    rechargeAmount = BigDecimal.ZERO;
                }
            }
            if(rechargeAmount.doubleValue()>0 && one.getCreditBalance().doubleValue()>0){
                BigDecimal amount = rechargeAmount;
                LambdaQueryWrapperX<TemporaryCreditDO> eqCredit = new LambdaQueryWrapperX<TemporaryCreditDO>()
                        .eq(TemporaryCreditDO::getCustomerId, one.getCustomerId())
                        .eq(TemporaryCreditDO::getStatus,2 ) //状态为已核销
                        .orderByDesc(TemporaryCreditDO::getCreateTime);
                List<TemporaryCreditDO> temporaryCreditDOS = temporaryCreditMapper.selectList(eqCredit);
                List<TemporaryCreditDO> updateList = new ArrayList<>();
                for (TemporaryCreditDO temporaryCreditDO : temporaryCreditDOS) {
                    if(amount.doubleValue()>= temporaryCreditDO.getCreditAmount().doubleValue()){
                        //核销授信金额
                        temporaryCreditDO.setStatus(3);
                        updateList.add(temporaryCreditDO);
                        amount = amount.subtract(temporaryCreditDO.getCreditAmount());
                    }
                }
                if(updateList.size()>0){
                    //批量更新 核销金额信息
                    temporaryCreditMapper.updateBatch(updateList);
                }

                if(rechargeAmount.compareTo(one.getCreditBalance())>=0){
                    rechargeAmount = rechargeAmount.subtract(one.getCreditBalance());
                    one.setCreditBalance(BigDecimal.ZERO);
                }else {
                    one.setCreditBalance(one.getCreditBalance().subtract(rechargeAmount));
                    rechargeAmount = BigDecimal.ZERO;
                }
            }
            if(rechargeAmount.doubleValue()>0){
                one.setCbValue(one.getCbValue().add(rechargeAmount));
            }
            this.saveOrUpdate(one);
            LogRecordContext.putVariable("customerCode", cusotmerCode);
            //记录日志
        } catch (Exception e) {
            log.error("客户充值异常,customercode:{}, 错误信息：{}", cusotmerCode, e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.ACTION_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }


    @Override
    @LogRecord(type = SYSTEM_PAYMENT_TYPE, subType = SYSTEM_PAYMENT_CUSTOMER_CREDIT, bizNo = "{{#customerCode}}",
            success = SYSTEM_PAYMENT_CUSTOMER_CREDIT_SUCCESS)
    @Transactional
    public void customerBalanceAddCredit(String cusotmerCode,BigDecimal creditAmount){
        String lockName = RedisLockConstants.CUSTOMER_FEE + cusotmerCode;
        boolean locked = false;

        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取客户余额锁失败,ockName:{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.ACTION_EXCEPTION, "get customer balance sync lock failed,"+lockName);
            }
            LambdaQueryWrapperX<CustomerBalanceDO> eq = new LambdaQueryWrapperX<CustomerBalanceDO>().eq(CustomerBalanceDO::getCustomerCode, cusotmerCode);
            CustomerBalanceDO one = this.getOne(eq);
            if(one == null){
                one = new CustomerBalanceDO();
                one.setCustomerCode(cusotmerCode);
                one.setCustomerId(0L);
                one.setCbCreditLine(BigDecimal.ZERO);
                one.setCreditBalance(BigDecimal.ZERO);
                one.setCbValue(BigDecimal.ZERO);
            }
            one.setCreditBalance(one.getCreditBalance().add(creditAmount));
            one.setCbValue(one.getCbValue().add(creditAmount));
            this.saveOrUpdate(one);
            LogRecordContext.putVariable("customerCode", cusotmerCode);
            LogRecordContext.putVariable("feeValue", creditAmount.doubleValue());
            //记录日志
        } catch (Exception e) {
            log.error("客户余额增加授信异常,customercode:{}, 错误信息：{}", cusotmerCode, e.getMessage());
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.ACTION_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
        }
    }

}
